Repository navigation
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🤖 CI report
|
There was a problem hiding this comment.
PostHog Review · Standard · reviewed bbf56be: 3 Should fix.
This is the only automatic review while this pull request is a draft. Comment @posthog review to review new changes sooner. Otherwise, a review of the new changes runs when you mark it ready for review.
See this review and its findings in PostHog Review. Comment @posthog review --deep for a Deep review. A Deep review takes longer and looks deeper than a Standard review.
| return TeamSendingHistory( | ||
| team_id=team_id, | ||
| sent=sum(counts.get(SENT_METRIC, 0) for counts in days.values()), | ||
| sent=sum(own_sends(counts) for counts in days.values()), |
There was a problem hiding this comment.
P2 · Floor sandbox subtraction per source before summing daily sends
When a workflow has unmatched email_sandbox_sent metrics for a day, fetch_app_metric_daily_totals_by_team merges them with other workflows before own_sends applies its floor. Those sandbox counts then subtract another workflow's real sends, understating promotion volume and distorting reputation rates. Subtract and floor per source and day before summing team totals.
| with transaction.atomic(): | ||
| state = SandboxSenderTenantState.objects.select_for_update().get(tenant_name=tenant_name) | ||
| tenant = (provider or SESProvider()).get_tenant_sending_state(tenant_name) |
There was a problem hiding this comment.
P2 · Bound SES calls and lock waits in the sandbox sync
During an SES slowdown, sync_sandbox_tenant_state holds the shared row lock across two API calls using boto3's default 60-second timeouts and retries. Concurrent webhook tasks block on that lock while occupying DEFAULT queue workers and database connections, delaying unrelated work. Bound the SES calls and use nonblocking locking with retry or coalescing.
|
|
||
| def reload_sandbox_sender_state_on_workers(tenant_name: str) -> None: | ||
| logger.info(f"Reloading sandbox sender state for tenant {tenant_name} on workers") | ||
| publish_message("reload-sandbox-sender-state", {"tenantName": tenant_name}) |
There was a problem hiding this comment.
P2 · Wire the sandbox reload message to a worker consumer
When reconciliation records DISABLED, this publishes to a channel that no worker subscribes to. Workers also never read SandboxSenderTenantState; SandboxEmailSender.canSend depends only on configuration, so they continue attempting sends against the paused tenant. Add a worker state reader and subscriber, and enforce that state before sending.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…afety/django Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…/django Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…afety/django Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
Changes
SandboxSenderOrganizationBlockrow in the Django admin. The provision endpoint then returns 404 for its projects.email_sandbox_sentfromemail_sent, floored at zero. Bounces and complaints still count.email_sandbox_sent. A team without sandbox sends still gets master's numbers, because that metric is 0 for it.db_constraint=False, so no hot table is locked.How did you test this code?
Test rationale: one mixed-traffic case per metric reader checks that sandbox sends drop out of the volume and allowance, and that an unconfigured deployment queries master's metric list. Tenant tests cover a changed state, an unknown tenant, and an empty tenant name.
Release status
Docs update
docs/internal/workflows-email-sending-tiers.mddescribes the block, the tenant mirror and the tier exclusion.🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: Claude Code, Claude Opus 5.5
Skills:
/django-migrations,/writing-tests,/writing-code-comments,/writing-pr-descriptionsMore context for AI agents
email_sandbox_sentbut no matchingemail_sentsubtracts nothing.email_sandbox_sentis zero and the numbers match master.own_sendsandsent_metric_namesinemail_sending_tier.py,sync_sandbox_tenant_stateinses_tenant_state.py,providers/ses.py.🤖 Generated with Claude Code